/*
 * CloudBeaver - Cloud Database Manager
 * Copyright (C) 2020-2025 DBeaver Corp and others
 *
 * Licensed under the Apache License, Version 2.0.
 * you may not use this file except in compliance with the License.
 */
import { injectable } from '@cloudbeaver/core-di';
import {
  ACTION_ADD,
  ACTION_CANCEL,
  ACTION_DELETE,
  ACTION_DUPLICATE,
  ACTION_REVERT,
  ACTION_SAVE,
  ActionService,
  DATA_CONTEXT_MENU,
  KEY_BINDING_ADD,
  KEY_BINDING_DELETE,
  KEY_BINDING_DUPLICATE,
  KEY_BINDING_REVERT,
  KeyBindingService,
  MenuService,
  type IAction,
} from '@cloudbeaver/core-view';
import { LocalizationService } from '@cloudbeaver/core-localization';
import type { IDataContextProvider } from '@cloudbeaver/core-data-context';

import { DatabaseEditChangeType, IDatabaseDataEditAction } from '../../../DatabaseDataModel/Actions/IDatabaseDataEditAction.js';
import { DATA_CONTEXT_DV_DDM } from '../../../DatabaseDataModel/DataContext/DATA_CONTEXT_DV_DDM.js';
import { DATA_CONTEXT_DV_DDM_RESULT_INDEX } from '../../../DatabaseDataModel/DataContext/DATA_CONTEXT_DV_DDM_RESULT_INDEX.js';
import { DATA_CONTEXT_DV_PRESENTATION, DataViewerPresentationType } from '../../../DatabaseDataModel/DataContext/DATA_CONTEXT_DV_PRESENTATION.js';
import type { IDatabaseDataModel } from '../../../DatabaseDataModel/IDatabaseDataModel.js';
import { DATA_VIEWER_DATA_MODEL_ACTIONS_MENU } from './DATA_VIEWER_DATA_MODEL_ACTIONS_MENU.js';
import { DataViewerViewService } from '../../DataViewerViewService.js';
import { IDatabaseDataSelectAction } from '../../../DatabaseDataModel/Actions/IDatabaseDataSelectAction.js';

@injectable(() => [ActionService, KeyBindingService, DataViewerViewService, LocalizationService, MenuService])
export class TableFooterMenuService {
  constructor(
    private readonly actionService: ActionService,
    private readonly keyBindingService: KeyBindingService,
    private readonly dataViewerViewService: DataViewerViewService,
    private readonly localizationService: LocalizationService,
    private readonly menuService: MenuService,
  ) {}

  register(): void {
    this.registerEditingActions();
    this.keyBindingService.addKeyBindingHandler({
      id: 'table-footer-delete',
      binding: KEY_BINDING_DELETE,
      contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
      isBindingApplicable(context, action) {
        return action === ACTION_DELETE;
      },
      handler: this.tableFooterMenuActionHandler.bind(this),
    });

    this.keyBindingService.addKeyBindingHandler({
      id: 'table-footer-revert',
      binding: KEY_BINDING_REVERT,
      contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
      isBindingApplicable(context, action) {
        return action === ACTION_REVERT;
      },
      handler: this.tableFooterMenuActionHandler.bind(this),
    });

    this.keyBindingService.addKeyBindingHandler({
      id: 'table-footer-add',
      binding: KEY_BINDING_ADD,
      contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
      isBindingApplicable(context, action) {
        return action === ACTION_ADD;
      },
      handler: this.tableFooterMenuActionHandler.bind(this),
    });

    this.keyBindingService.addKeyBindingHandler({
      id: 'table-footer-duplicate',
      binding: KEY_BINDING_DUPLICATE,
      contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
      isBindingApplicable(context, action) {
        return action === ACTION_DUPLICATE;
      },
      handler: this.tableFooterMenuActionHandler.bind(this),
    });

    this.dataViewerViewService.registerAction(ACTION_DELETE, ACTION_REVERT, ACTION_ADD, ACTION_DUPLICATE);
  }

  private registerEditingActions() {
    this.menuService.addCreator({
      menus: [DATA_VIEWER_DATA_MODEL_ACTIONS_MENU],
      contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
      isApplicable(context) {
        const model = context.get(DATA_CONTEXT_DV_DDM)!;
        const resultIndex = context.get(DATA_CONTEXT_DV_DDM_RESULT_INDEX)!;
        const presentation = context.get(DATA_CONTEXT_DV_PRESENTATION);

        return !model.isReadonly(resultIndex) && !presentation?.readonly && (!presentation || presentation.type === DataViewerPresentationType.Data);
      },
      getItems(context, items) {
        return [ACTION_ADD, ACTION_DUPLICATE, ACTION_DELETE, ACTION_REVERT, ACTION_SAVE, ACTION_CANCEL, ...items];
      },
    });
    this.actionService.addHandler({
      id: 'data-base-editing-handler',
      contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
      actions: [ACTION_ADD, ACTION_DUPLICATE, ACTION_DELETE, ACTION_REVERT, ACTION_SAVE, ACTION_CANCEL],
      isActionApplicable(context, action) {
        const model = context.get(DATA_CONTEXT_DV_DDM)!;
        const resultIndex = context.get(DATA_CONTEXT_DV_DDM_RESULT_INDEX)!;
        const currentMenu = context.getOwn(DATA_CONTEXT_MENU);
        if (currentMenu !== undefined && currentMenu !== DATA_VIEWER_DATA_MODEL_ACTIONS_MENU) {
          return false;
        }

        if (model.isReadonly(resultIndex)) {
          return false;
        }

        const editor = model.source.tryGetAction(resultIndex, IDatabaseDataEditAction);

        if (!editor) {
          return false;
        }

        switch (action) {
          case ACTION_DUPLICATE:
          case ACTION_ADD: {
            return editor.hasFeature('add');
          }
          case ACTION_DELETE: {
            return editor.hasFeature('delete');
          }
          case ACTION_REVERT: {
            return editor.hasFeature('revert');
          }
        }
        return true;
      },
      isDisabled(context, action) {
        const model = context.get(DATA_CONTEXT_DV_DDM)!;
        const resultIndex = context.get(DATA_CONTEXT_DV_DDM_RESULT_INDEX)!;

        if (model.isLoading() || model.isDisabled(resultIndex) || !model.source.getResult(resultIndex)) {
          return true;
        }

        switch (action) {
          case ACTION_DUPLICATE: {
            const selectedElements = getActiveElements(model, resultIndex);

            return selectedElements.length === 0;
          }
          case ACTION_DELETE: {
            const editor = model.source.tryGetAction(resultIndex, IDatabaseDataEditAction);
            const selectedElements = getActiveElements(model, resultIndex);

            const canEdit =
              model.hasElementIdentifier(resultIndex) || selectedElements.every(key => editor?.getElementState(key) === DatabaseEditChangeType.add);

            if (!editor || !canEdit) {
              return true;
            }

            return selectedElements.length === 0 || !selectedElements.some(key => editor.getElementState(key) !== DatabaseEditChangeType.delete);
          }
          case ACTION_REVERT: {
            const editor = model.source.tryGetAction(resultIndex, IDatabaseDataEditAction);

            if (!editor) {
              return true;
            }

            const selectedElements = getActiveElements(model, resultIndex);

            return (
              selectedElements.length === 0 ||
              !selectedElements.some(key => {
                const state = editor.getElementState(key);

                if (state === DatabaseEditChangeType.add) {
                  return editor.isElementEdited(key);
                }

                return state !== null;
              })
            );
          }
          case ACTION_SAVE:
          case ACTION_CANCEL: {
            const editor = model.source.tryGetAction(resultIndex, IDatabaseDataEditAction);

            return !editor?.isEdited();
          }
        }

        return false;
      },
      getActionInfo: this.tableFooterMenuGetActionInfo.bind(this),
      handler: this.tableFooterMenuActionHandler.bind(this),
    });
  }

  private tableFooterMenuActionHandler(context: IDataContextProvider, action: IAction) {
    const model = context.get(DATA_CONTEXT_DV_DDM)!;
    const resultIndex = context.get(DATA_CONTEXT_DV_DDM_RESULT_INDEX)!;
    const editor = model.source.tryGetAction(resultIndex, IDatabaseDataEditAction);

    if (!editor) {
      return;
    }
    const select = model.source.tryGetAction(resultIndex, IDatabaseDataSelectAction);
    const selectedElements = getActiveElements(model, resultIndex);

    switch (action) {
      case ACTION_ADD: {
        editor.add(select?.getFocusedElement());
        break;
      }
      case ACTION_DUPLICATE: {
        editor.duplicate(...selectedElements);
        break;
      }
      case ACTION_DELETE: {
        editor.delete(...selectedElements);
        break;
      }
      case ACTION_REVERT: {
        editor.revert(...selectedElements);
        break;
      }
      case ACTION_SAVE:
        model.save().catch(() => {});
        break;
      case ACTION_CANCEL: {
        editor.clear();
        break;
      }
    }
  }

  private tableFooterMenuGetActionInfo(context: IDataContextProvider, action: IAction) {
    const t = this.localizationService.translate;
    switch (action) {
      case ACTION_ADD:
        return {
          ...action.info,
          label: '',
          icon: '/icons/data_add_sm.svg',
          tooltip: t('data_viewer_action_edit_add'),
        };
      case ACTION_DUPLICATE:
        return {
          ...action.info,
          label: '',
          icon: '/icons/data_add_copy_sm.svg',
          tooltip: t('data_viewer_action_edit_add_copy'),
        };
      case ACTION_DELETE:
        return { ...action.info, label: '', icon: '/icons/data_delete_sm.svg', tooltip: t('data_viewer_action_edit_delete') };
      case ACTION_REVERT:
        return { ...action.info, label: '', icon: '/icons/data_revert_sm.svg', tooltip: t('data_viewer_action_edit_revert') };
      case ACTION_SAVE:
        return { ...action.info, icon: 'table-save' };
      case ACTION_CANCEL:
        return { ...action.info, icon: '/icons/data_revert_all_sm.svg', tooltip: t('data_viewer_value_revert_title') };
    }

    return action.info;
  }
}

function getActiveElements(model: IDatabaseDataModel, resultIndex: number): unknown[] {
  const select = model.source.tryGetAction(resultIndex, IDatabaseDataSelectAction);

  return select?.getActiveElements() ?? [];
}
